/**
 * Model Created on 2022年4月30日
 * <p>
 * Copyright (c) 2018 by Kunrong.HOo.
 */
package com.seaside.park.service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.seaside.park.api.RemoteStatusAPI;
import com.seaside.park.component.ReadYZNo;
import com.seaside.parkCommon.bean.*;
import com.seaside.parkCommon.mapper.*;
import com.seaside.parkCommon.util.HttpUtil;
import com.seaside.parkCommon.vo.yz.OrderMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import com.alibaba.fastjson.JSON;
import com.seaside.common.bean.Result;
import com.seaside.park.vo.notify.CarInOutVO;
import com.seaside.park.vo.notify.OrderVO;

import lombok.extern.slf4j.Slf4j;

/**
 * @author <a href="mailto:hoocoral@hotmail.com">Kunrong.HOo</a>
 * @version 1.0
 */
@Slf4j
@Service
public class ParkingService{

    @Autowired
    private NotifyService notifyService;
    @Autowired
    private ParkPlaceMapper parkPlaceMapper;
    @Autowired
    private RemoteService remoteService;
    @Autowired
    private ReserveOrderService orderService;
    @Autowired
    private ParkInfoMapper parkInfoMapper;
    @Autowired
    private ReserveOrderMapper reserveOrderMapper;
    @Autowired
    private WhiteListMapper whiteListMapper;

    //所有的分时临停车位队列映射表
    public static LinkedHashMap<Integer, Queue<String>> AllOrderParkPlace;
    //所有未分配成功，下次优先分配的列表映射
    public static LinkedHashMap<Integer, Queue<String>> AllNoOrderParkPlace;

    /**
     * 所有的分时临停车场的分配次数计数
     * Key：车场编号
     * Value：进入的可分配车辆数目
     */
    public static LinkedHashMap<Integer, Integer> OrderCounts;

    //队列初始化标志
    public static boolean generated = false;
    //日期格式


    /**
     * 车辆进场处理
     *
     * @param param
     * @return
     */
    public void enterParkZone (CarInOutVO param){

        synchronized (this) {
            if (!generated) {
                getParkPlaceForOrder();
            }
        }

        //晋润国际入场
        ParkZone parkZone = ReadYZNo.yzcodeProfile.get(param.getParkZoneId());
        boolean ifOrderOK = false;
        //临时停车处理
        if (param.getCarPlateType().equals("1")) {
            return;
        }

        //进入车辆可以分配，开始计数
        int count = OrderCounts.get(parkZone.getZoneId()) + 1;
        OrderCounts.put(parkZone.getZoneId(), count);

        //获取车场的队列
        Queue<String> parkPlaceListQuery = AllOrderParkPlace.get(parkZone.getZoneId());
        //上一轮分配，但车位被车辆占用，进入该队列
        Queue<String> noOrderOKQuery = AllNoOrderParkPlace.get(parkZone.getZoneId());

        /*各个停车场权重不同*/
        if (parkZone.getZoneId() == 14) {
            if (count % 2 != 0) {
                SendOrderSms(param.getCarPlate(), "临停车位", parkZone.getZoneName());
                RemoteStatusAPI.distribution.put(param.getCarPlate(), "临停车位");
                log.info("{}分配临停车位!", parkZone.getZoneName());
                return;
            }
        }
        //润城一区
        else if (parkZone.getZoneId() == 8) {
            if (count % 8 != 0) {
                SendOrderSms(param.getCarPlate(), "临停车位", parkZone.getZoneName());
                RemoteStatusAPI.distribution.put(param.getCarPlate(), "临停车位");
                log.info("{}分配临停车位!", parkZone.getZoneName());
                return;
            }
        }
        //润城四大道商业
        else if (parkZone.getZoneId() == 9) {
            if (count % 20 != 0) {
                SendOrderSms(param.getCarPlate(), "临停车位", parkZone.getZoneName());
                RemoteStatusAPI.distribution.put(param.getCarPlate(), "临停车位");
                log.info("{}分配临停车位!", parkZone.getZoneName());
                return;
            }
        }
        //润城五大道
        else if (parkZone.getZoneId() == 10) {
            if (count % 25 != 0) {
                SendOrderSms(param.getCarPlate(), "临停车位", parkZone.getZoneName());
                RemoteStatusAPI.distribution.put(param.getCarPlate(), "临停车位");
                log.info("{}分配临停车位!", parkZone.getZoneName());
                return;
            }
        }
        //润城四大道住宅
        else if (parkZone.getZoneId() == 12) {
            if (count % 40 != 0) {
                SendOrderSms(param.getCarPlate(), "临停车位", parkZone.getZoneName());
                RemoteStatusAPI.distribution.put(param.getCarPlate(), "临停车位");
                log.info("{}分配临停车位!", parkZone.getZoneName());
                return;
            }
        }

        //循环递归分配，取得结果
        ifOrderOK = OrderPlace(param, parkPlaceListQuery, noOrderOKQuery, 0);
        //如果预约车位没有空的，队列中车位也已经全部停放车辆
        if (!ifOrderOK) {
            //通过云智平台给车主发送通知信息
            log.info(parkZone.getZoneName() + "预约车位分配已满，开始分配临停车位!");
            SendOrderSms(param.getCarPlate(), "临停车位", parkZone.getZoneName());
            RemoteStatusAPI.distribution.put(param.getCarPlate(), "临停车位");
        }
    }

/**
 * 车辆出场处理
 *
 * @param param
 * @return
 */
    public Result<CarInOutVO> leaveParkZone (CarInOutVO param){

        if (StringUtils.isBlank(param.getCarPlate())) {
            log.warn("车牌号为空！");
            return Result.Error("车牌号为空~！");
        }

        List<ParkPlace> parkPlaceList = parkPlaceMapper.getParkPlaceWithCarPlate(param.getCarPlate());
        if (!CollectionUtils.isEmpty(parkPlaceList)) {

            parkPlaceList.forEach(place -> {
                if (place != null) {

                    // 清理预约列表
                    String tempList = place.getTempList();
                    if (StringUtils.isNotBlank(tempList)) {
                        tempList = tempList.replace(param.getCarPlate(), "");
                        tempList = tempList.replace("||", "|");
                        place.setTempList(tempList);
                        //更新边缘节点的信息
                        log.info("###【" + param.getCarPlate() + "】离开停车场，清理预约停车位【" + place.getParkNo() + "】，剩余预约列表 => 【" + place.getTempList() + "】");
                    }

                    // 清理当前停车位
                    if (param.getCarPlate().equals(place.getCurrPlate())) {
                        place.setCurrPlate(null);

                        log.info("###【" + param.getCarPlate() + "】离开停车场，清理停车位【" + place.getParkNo() + "】");
                    }
                    remoteService.leavePark(param.getParkZoneId(), place.getParkNo(), param.getCarPlate());
                    place.setLastChangeTime(new Date());
                    parkPlaceMapper.updateById(place);
                }
            });

            // 设置订单结束
            orderService.setOrderFinished(param.getCarPlate());
            //清除预约记录
            RemoteStatusAPI.cleanDistribution(param.getCarPlate());

            return Result.OK("处理成功！", param);
        }
        return Result.Error("未找到车位信息！");
    }

/**
 * 预约泊位
 *
 * @param order
 * @return
 */
    public Result<ParkPlace> reservationBerth (OrderVO order){

        ParkPlace parkPlace = parkPlaceMapper.getParkPlaceInfo(order.getParkZoneId(), order.getBerthNumber());

        //记录车牌号和手机号码
        ParkInfo parkInfo = new ParkInfo();
        parkInfo.setCarPlate(order.getCarPlate());
        parkInfo.setTelNum(order.getPhoneNumber());
        parkInfoMapper.insert(parkInfo);

        //远程打开预约灯光
        remoteService.changeLampStatus(order.getParkZoneId(), order.getBerthNumber(), "ON");

        if (parkPlace == null) {
            log.warn("未找到车位信息：【" + JSON.toJSONString(order) + "】");
            return Result.Error("未找到车位信息！");
        }

        String tempList = parkPlace.getTempList();
        if (StringUtils.isBlank(tempList)) {
            parkPlace.setTempList(order.getCarPlate());
        } else {
            tempList += ("|" + order.getCarPlate());
            parkPlace.setTempList(tempList);
        }
        parkPlace.setLastChangeTime(new Date());
        parkPlaceMapper.updateById(parkPlace);

        log.info("###【" + order.getCarPlate() + "】预约【" + order.getBerthNumber() + "】，预约列表 => 【" + parkPlace.getTempList() + "】");

        return Result.OK("处理成功！", parkPlace);
    }

/**
 * 通过云智发送给车主的通知短信
 */
    private void SendOrderSms (String carPlate, String parkNo, String parkName){

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日HH点mm分");
        LocalDateTime nowDateTime = LocalDateTime.now();

        OrderMessage message = new OrderMessage();
        String[] messageVariable = new String[4];
        messageVariable[0] = carPlate;
        messageVariable[1] = parkName;
        messageVariable[2] = nowDateTime.format(formatter);
        messageVariable[3] = parkNo;
        message.setMessageVariable(messageVariable);

        String jsonString = "[" + JSON.toJSONString(message, SerializerFeature.WriteMapNullValue) + "]";
        if (HttpUtil.jsonPost("https://rain.yzparking.cn/notice/barui/receiveCallData", jsonString)) {
            log.info("预约车位发送短信成功!");
        } else {
            log.error("预约车位发送短信失败!");
        }
    }

/**
 * 从分配队列中进行分配
 */
    private boolean OrderPlace (CarInOutVO
    param, Queue < String > parkPlaceListQuery, Queue < String > noOKParkPlaceListQuery,int rounds){
        String parkPlacePreOrderNo;
        ParkPlace parkPlace;
        boolean ifPrior = false;

        ParkZone parkZone = ReadYZNo.yzcodeProfile.get(param.getParkZoneId());

        parkPlacePreOrderNo = parkPlaceListQuery.poll();

        /*
        if(noOKParkPlaceListQuery.isEmpty() || rounds>=noOKParkPlaceListQuery.size()){
            //从所有分时临停车位队列中，按顺序分配车位，取出并移除队列
            parkPlacePreOrderNo = parkPlaceListQuery.poll();
        }else{
            parkPlacePreOrderNo = noOKParkPlaceListQuery.poll();
            ifPrior = true;
            rounds = rounds +1;
        }
        */
        //查看车位是否为空
        if (parkPlacePreOrderNo == null) {
            log.error("预分配列表中有空值!" + parkZone.getZoneName());
            return false;
        } else {
            log.info(parkZone.getZoneName() + "预分配车位是:" + parkPlacePreOrderNo);
        }
        parkPlace = getParkPlaceByParkNo(parkPlacePreOrderNo, parkZone.getZoneId());
        /**
         * 如果车位没有被车辆占用，则可以分配，分配给用户，同时发送短信，同时发送云智落位
         * 第一步：形成订单
         */
        //生成订单,要考虑异常
        try {
            ReserveOrder order = new ReserveOrder();
            order.setCarPlate(param.getCarPlate());
            order.setBerthNumber(parkPlacePreOrderNo);
            order.setOrderTime(new Date());
            order.setOrderInTime(new Date());
            order.setParkPlaceId(parkPlace.getParkId());
            //四大道测试ID
            order.setParkZoneId(parkZone.getZoneNo());
            reserveOrderMapper.insert(order);
            //修改泊位预约列表
            String tempList = parkPlace.getTempList();
            if (StringUtils.isBlank(tempList)) {
                parkPlace.setTempList(order.getCarPlate());
            } else {
                tempList += ("|" + order.getCarPlate());
                parkPlace.setTempList(tempList);
            }
            parkPlace.setIfOrder(1);
            parkPlace.setLastChangeTime(new Date());
            parkPlaceMapper.updateById(parkPlace);

            //远程打开预约灯光
            remoteService.changeLampStatus(param.getParkZoneId(), order.getBerthNumber(), "ON");
            //通过云智平台给车主发送通知信息
            if (parkPlace.getCurrPlate().equalsIgnoreCase("") || null == parkPlace.getCurrPlate()) {
                SendOrderSms(param.getCarPlate(), parkPlacePreOrderNo, parkZone.getZoneName());
            } else {
                SendOrderSms(param.getCarPlate(), "临停车位", parkZone.getZoneName());
            }

            //发送落位信息，分配即认为落位，费用清分到该车位
            notifyService.NotifyYZ(parkPlacePreOrderNo, param.getParkZoneId(), param.getCarPlate(), "IN");
            //记录车位分配情况
            RemoteStatusAPI.distribution.put(param.getCarPlate(), parkPlacePreOrderNo);
            //分配完成，车位号重新回到队列
            parkPlaceListQuery.offer(parkPlacePreOrderNo);
            log.info("预分配车位分配成功!分配队列" + ifPrior);
            return true;

        } catch (Exception e) {
            log.error("xxxxxxxxxx分配车位出现错误！车位号是：" + parkPlacePreOrderNo + e.getMessage());
            //分配完成，车位号重新回到队列
            parkPlaceListQuery.offer(parkPlacePreOrderNo);
            return false;
        }
    }

/**
 * 从数据库获取所有可以用来分配的车位
 */
    public void getParkPlaceForOrder () {
        AllOrderParkPlace = new LinkedHashMap<>();
        AllNoOrderParkPlace = new LinkedHashMap<>();
        OrderCounts = new LinkedHashMap<>();
        QueryWrapper<ParkPlace> wrapper = new QueryWrapper<>();
        wrapper.eq("valid", 1).eq("park_type", 0);
        List<ParkPlace> AllParkList = parkPlaceMapper.selectList(wrapper);
        for (ParkPlace parkPlace : AllParkList) {

            if (AllNoOrderParkPlace.get(parkPlace.getZoneId()) == null) {
                Queue<String> NoparkPlaceQueue = new LinkedList<>();
                AllNoOrderParkPlace.put(parkPlace.getZoneId(), NoparkPlaceQueue);
            }

            if (OrderCounts.get(parkPlace.getZoneId()) == null) {
                OrderCounts.put(parkPlace.getZoneId(), 0);
            }

            if (AllOrderParkPlace.get(parkPlace.getZoneId()) == null) {
                Queue<String> AllparkPlaceQueue = new LinkedList<>();
                AllparkPlaceQueue.offer(parkPlace.getParkNo());
                AllOrderParkPlace.put(parkPlace.getZoneId(), AllparkPlaceQueue);
            } else {
                Queue<String> parkPlaceQueue = AllOrderParkPlace.get(parkPlace.getZoneId());
                parkPlaceQueue.offer(parkPlace.getParkNo());
                AllOrderParkPlace.put(parkPlace.getZoneId(), parkPlaceQueue);
            }
        }
        generated = true;
        log.info("生成每一个车场可预约车位的映射表");
        log.info("晋润国际可预约车位数量" + AllOrderParkPlace.get(14).size());
        log.info("俊寓可代泊车位数量" + AllOrderParkPlace.get(7).size());
    }

/**
 * 根据车位号，车场编号获取车位对象
 */
    public ParkPlace getParkPlaceByParkNo (String parkNo,int zoneId){
        //根据泊位号查询车位空余情况
        QueryWrapper<ParkPlace> wrapper = new QueryWrapper<>();
        wrapper.eq("park_no", parkNo).eq("zone_id", zoneId);
        List<ParkPlace> parkPlaceList = parkPlaceMapper.selectList(wrapper);
        if (null != parkPlaceList) {
            return parkPlaceList.get(0);
        }
        return null;
    }
}